#import "BWPannerView.h"
#import <BSL/BSL.h>

@interface BWPannerView(ForwardReferences)

- (void) boundsDidChangeNotification: (NSNotification *) notification;

@end // ForwardReferences

@implementation BWPannerView


- (void) dealloc
{
    NSNotificationCenter *center;

    center = [NSNotificationCenter defaultCenter];
    [center removeObserver: self];

    [scrollView release];
    [transformer release];

    [super dealloc];

} // dealloc



// more than meets the eye
- (void) setupTransformer
{
    NSClipView *contentView;
    contentView = [scrollView contentView];
        
    NSRect bounds = [self bounds];

    // pretend we're a little smaller so there's a border around
    // the target view
    NSRect innerBounds = NSInsetRect (bounds, 6, 6);

    // the total size of the view being scrolled
    NSRect documentRect = [contentView documentRect];
    documentRect.origin = NSZeroPoint;

    // scale the total area being scrolled into our rectangle
    NSRect fitRect = BWFitRectInto (documentRect, innerBounds);

    [transformer release];
    [inverse release];
    transformer = [NSAffineTransform transform];
    // adjust for the insetitude
    [transformer translateXBy: 6.5   yBy: 6.5];

    float h, w;
    w = (innerBounds.size.width - fitRect.size.width) / 2.0;
    h = (innerBounds.size.height - fitRect.size.height) / 2.0;

    [transformer translateXBy: w
                 yBy: h];

    // ratio of shrinkitude to make it fit
    float xscale, yscale;
    xscale = fitRect.size.width / documentRect.size.width;
    yscale = fitRect.size.height / documentRect.size.height;

    [transformer scaleXBy: xscale
                 yBy: yscale];
    [transformer retain];

    inverse = [transformer copy];
    [inverse invert];

} // setupTransformer


- (void) drawInnerView
{
    [NSGraphicsContext saveGraphicsState]; {
        NSClipView *contentView = [scrollView contentView];
        NSView *view = [contentView documentView];

        [transformer concat];

        [view drawRect: [view bounds]];

    } [NSGraphicsContext restoreGraphicsState];

} // drawInnerView


- (void) drawRect: (NSRect) rect
{
    NSRect bounds = [self bounds];

    [[NSColor whiteColor] set];
    [NSBezierPath fillRect: bounds];

    [[NSColor blackColor] set];

    if (scrollView != nil) {
        [self setupTransformer];

        NSClipView *contentView;
        contentView = [scrollView contentView];

        NSRect documentRect = [contentView documentRect];
        documentRect.origin = NSZeroPoint;

        // the rectangle that's currently being displayed
        NSRect visibleRect;
        visibleRect = [scrollView documentVisibleRect];

        // don't want to concat the transform, 'cause if it's a big
        // scrollable area, the lines will get cranked down
        NSRect xdocumentRect, xvisibleRect;
        xdocumentRect.origin = [transformer transformPoint: documentRect.origin];
        xdocumentRect.size = [transformer transformSize: documentRect.size];
        xvisibleRect.origin = [transformer transformPoint: visibleRect.origin];
        xvisibleRect.size = [transformer transformSize: visibleRect.size];

        [self drawInnerView];

        [NSBezierPath strokeRect: xdocumentRect];
        [[NSColor redColor] set];
        xvisibleRect.origin.x += 0.5;
        xvisibleRect.origin.y += 0.5;
        [NSBezierPath strokeRect: xvisibleRect];
    }
        
    [[NSColor blackColor] set];
    [NSBezierPath strokeRect: bounds];

} // drawRect



- (void) setScrollView: (NSScrollView *) s
{
    scrollView = [s retain];
    [scrollView setPostsBoundsChangedNotifications: YES];
    [[scrollView contentView] setPostsBoundsChangedNotifications: YES];
    [[scrollView contentView] setPostsFrameChangedNotifications: YES];
    [[[scrollView contentView] documentView] setPostsFrameChangedNotifications: YES];

    NSNotificationCenter *center = [NSNotificationCenter defaultCenter] ;
    [center addObserver: self 
            selector: @selector(boundsDidChangeNotification:)
            name: NSViewBoundsDidChangeNotification 
            object: [scrollView contentView]];

    [center addObserver: self 
            selector: @selector(boundsDidChangeNotification:)
            name: NSViewFrameDidChangeNotification 
            object: [scrollView contentView]];

    [center addObserver: self 
            selector: @selector(boundsDidChangeNotification:)
            name: NSViewFrameDidChangeNotification 
            object: [[scrollView contentView] documentView]];


    [self setNeedsDisplay: YES];

} // setScrollView


- (void) boundsDidChangeNotification: (NSNotification *) notification
{
    [self setNeedsDisplay: YES];

} // boundsDidChangeNotification



- (BOOL) isFlipped
{
    // drive from the scrolled view?
    return (YES);
} // isFlipped



- (void) scrollToMouse: (NSEvent *) event
{
    NSPoint mouse;
    mouse = [self convertPoint: [event locationInWindow]  fromView: nil];

    NSPoint viewPoint;
    viewPoint = [inverse transformPoint: mouse];

    NSRect visibleRect;
    visibleRect = [scrollView documentVisibleRect];

    NSRect documentRect = [[scrollView contentView] documentRect];
    documentRect.origin = NSZeroPoint;

    viewPoint.x -= visibleRect.size.width / 2.0;
    viewPoint.y -= visibleRect.size.height / 2.0;

    if (viewPoint.x < 0.0) {
        viewPoint.x = 0.0;
    }
    if (viewPoint.y < 0.0) {
        viewPoint.y = 0.0;
    }

    NSPoint bottomRight;
    bottomRight.x = viewPoint.x + visibleRect.size.width;
    bottomRight.y= viewPoint.y + visibleRect.size.height;

    if (bottomRight.x > documentRect.size.width) {
        viewPoint.x -= (bottomRight.x - documentRect.size.width);
    }
    if (bottomRight.y > documentRect.size.height) {
        viewPoint.y -= (bottomRight.y - documentRect.size.height);
    }

    viewPoint.x = truncf (viewPoint.x);
    viewPoint.y = truncf (viewPoint.y);

    [[scrollView contentView] scrollToPoint: viewPoint];
    [scrollView reflectScrolledClipView: [scrollView contentView]];
 
} // scrollToMouse


- (void) mouseDown: (NSEvent *) event
{
    [self scrollToMouse: event];
} // mouseDown


- (void) mouseDragged: (NSEvent *) event
{
    [self scrollToMouse: event];
} // mouseDragged



@end // BWPannerView

